﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
using FinPlusDBConn;
using FinPlusCommon;
using Messaging2;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusWPFControls
{
    public partial class FinPlusWindow : Window, IFinPlusComp
    {
        public string Id { get { return _id == null ? "Window" : _id; } set { _id = value; } }
        public string[] States { get { return new string[] { "Height", "Width", "Left", "Top", "TabPosition", "ScaleValue", "RotateAngle" }; } }
        public string[] TestStates { get { return new string[] { "TestString", "ChildrenCount", "Height", "Width", "Left", "Top" }; } }
        public IFunctions Adaptor { get { return _app.Adaptor; } }
        public string TestString { get { return Adaptor.ToString(); } }
        public bool IsValid { get; set; }
        public string ContainerId { get; set; }

        public int ChildrenCount { get { return grid.Children.Count; } }
        public double TabPosition { get { return padding.Width; } set { padding.Width = value; } }
        public double ScaleValue { get { return _transform.ScaleValue; } set { _transform.ScaleValue = value; } }
        public double RotateAngle { get { return _transform.RotateAngle; } set { _transform.RotateAngle = value; } }
        public readonly static RoutedCommand CmdRoute = new RoutedCommand();
        public EventHandler WindowClose;
        public string Guid;

        private const string XmlNode = "root/current";
        private enum DockControls { FinPlusControl, Default }
        private FinPlusView _app;
        private FinPlusTransform _transform;
        private IFunction _func;
        private ITransport _tran;
        private string _id, _user = Environment.UserName, _configPath;
        private double _mouseStart;
        private bool _isTabShift;

        //construct
        public FinPlusWindow()
        {
            InitializeComponent();
            scalebar.Children.Add(_transform = new FinPlusTransform());
            Cursor = ((TextBlock)this.Resources["CPoint"]).Cursor;
        }

        //common control interface
        public void Dock(object control, string dock)
        {
            try
            {
                switch (u.EnumParse<DockControls>(control.GetType().BaseType.Name, DockControls.Default))
                {
                    case DockControls.FinPlusControl: BuildView((FinPlusControl)control, dock); break;
                    case DockControls.Default: grid.Children.Add((UserControl)control); break;
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, _id, _configPath,  e.Message, e);
                grid.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        public virtual void CmdRouting(object s, ExecutedRoutedEventArgs a)
        {
            try
            {
                CmdRouter(a.Parameter.ToString(), s);
            }
            catch (CmdRouterException e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, _configPath, s.ToString(), e);
            }
        }

        public virtual void CmdRouter(string command, object control)
        {
            try
            {
                var tag = ((Control)control).Tag;
                CmdExecute(new Function(command, Id, _user, -1, tag == null ? string.Empty : tag.ToString()));
            }
            catch (CmdRouterException e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, _user, command, e);
            }
        }


        public virtual void CmdRouter(string command)
        {
            CmdExecute(new Function(command, _id, _user));
        }

        public void CmdExecute(IFunction command)
        {
            try
            {
                if (_app != null)
                    _app.CmdCapture(command);

                switch ((Cmds)Enum.Parse(typeof(Cmds), command.Name, true))
                {
                    case Cmds.Maximize: Maximize(); break;
                    case Cmds.Minimize: WindowState = WindowState.Minimized; break;
                    case Cmds.Close: Dispose(); break;
                    case Cmds.Home: Home(); break;
                    case Cmds.Save: SaveStates(); break;
                    case Cmds.Load: LoadState(); break;
                    case Cmds.Title: if (!_isTabShift) this.DragMove(); break;
                    case Cmds.TitleStartShift: _mouseStart = Mouse.GetPosition(null).X; _isTabShift = true; break;
                    case Cmds.TitleShift: TitleShift(); break;
                    case Cmds.TitleStopShift: _isTabShift = false; break;
                    default: throw new Exception(string.Format("event not recognised {0}", command.Name));
                }
            }
            catch (CmdExecuteException e)
            {
               IsValid = log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        public void Dispose()
        {
            try
            {
                if (_tran != null)
                {
                    _tran.Listener.Stop(Guid);
                    _tran.Publish(new Message(Guid, "OnClose"));
                }
                if (_app != null)
                    _app.Dispose();
                if (WindowClose != null)
                    WindowClose.Invoke(null, new EventArgs());
                if (CheckAccess())
                    this.Close();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        //public
        public static void OpenEditor(IFunction func, string name, u.Env env)//used by enterpise solutions
        {
            try
            {
                var thread = new Thread(new ThreadStart(() =>
                {
                    SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
                    var window = new FinPlusWindow();
                    window.Dock(new FinPlusFuncEditor(null, func, window.SetTitle, name, env), name);
                    window.Closed += (s, a) => Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
                    window.Topmost = true;
                    window.Show();
                    Dispatcher.Run();
                }));
                thread.SetApartmentState(ApartmentState.STA);
                thread.IsBackground = true;
                thread.Start();
            }
            catch (Exception e)
            {
               log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        public static void OpenEditor(IFunction func, IFinPlusDBConn conn, string name, u.Env env)//used by excel booker
        {
            try
            {
                var thread = new Thread(delegate()
                {
                    var window = new FinPlusWindow();
                    window.Dock(new FinPlusFuncEditor(null, func, window.SetTitle, name, env), "top");
                    window.Topmost = true;
                    window.Show();
                    Dispatcher.Run();
                });
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                throw new Exception("Booker window failed to open ", e);
            }
        }

        public static Process OpenWindow(string configPath, string name, string guid, string parms)
        {
            try
            {
                if(Debugger.IsAttached)
                    new FinPlusWindow().LoadView(configPath, name, guid, false, parms);
                else
                    return Process.Start(new ProcessStartInfo() { FileName = "FinPlusClientShell.exe", Arguments = string.Format("{0} {1} {2} {3}", configPath, name, guid, parms) });
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, e.Message, e);
            }

            return null;
        }

        public void LoadView(string configPath, string name,  string guid = "", bool isDialog = false, string parms = "")
        {
            try
            {
                _configPath = configPath;
                u.Env env;
                var config = u.ConfigXml(configPath + ".config", out env);

                Guid = guid;
                EventListen(config);
                BuildView(configPath, name, parms, env);
                LoadState();

                if (isDialog)
                    base.ShowDialog();
                else
                    base.Show();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, e.Message, e);
            }
        }

        public void SaveStates()
        {
            var funcsState = FinPlusDInject.Get.ExtractStates(_func, Environment.UserName);
            funcsState.ToXmlDoc(XmlNode).Save(_configPath + ".state");
        }

        public void LoadState()
        {
            try
            {
                _func = new Function("FinPlusWindow", "finPlusWindow", Environment.UserName) { Object = finPlusWindow };
                FinPlusDInject.Get.InjectStates(_func, new Functions(_configPath + ".state", XmlNode));
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, e.Message, e);
            }
        }

        //private
        private void BuildView(IFinPlusComp control, string name)
        {
            _configPath = u.GetAssemblyPath(Assembly.GetExecutingAssembly()) + "FinPlusViews\\" + name;
            _app = new FinPlusView();
            _app.Dock(control, string.Empty);
            grid.Children.Add(_app);
            LoadState();
        }

        private void BuildView(string configPath, string name, string parms, u.Env env = u.Env.unknown)
        {
            _configPath = configPath;
            titleText.Text = name;

            grid.Children.Add(_app = new FinPlusView(configPath, name, parms, env));
        }

        private void SetTitle(string title)
        {
            titleText.Text = title;
        }

        private void Home()
        {
            WindowState = WindowState.Normal;
            Activate();
            Topmost = true;
            Topmost = false;
            Focus();
        }

        private void Maximize()
        {
            switch(WindowState)
            {
                case  WindowState.Minimized: WindowState = WindowState.Normal; break;
                case  WindowState.Maximized: WindowState = WindowState.Normal;break;
                case  WindowState.Normal: WindowState = WindowState.Maximized;break;
            }
            Activate();
        }

        private void EventListen(u.Config config)
        {
            _tran = new Transport(config["localmessagingsettings"]["address"], int.Parse(config["localmessagingsettings"]["port"]));
            _tran.Listener.MessageRecieved += EventMessage;
            _tran.Listen(Guid);
        }

        private void TitleShift()
        {
            if (!_isTabShift)
                return;

            var width = _mouseStart - Mouse.GetPosition(null).X;
            
            if (width > 0) padding.Width = width;
        }

        //cmds
        private enum Cmds { Maximize, Minimize, Close, Home, Save, Load, Title, TitleStartShift, TitleShift, TitleStopShift}

        //events
        public void Command_OnInject(object s, FunctionEventArgs a) { this.Dispatcher.Invoke(DispatcherPriority.Normal,(Action)(() => { CmdExecute(a.Function); })); }
        private void Window_OnHome(object s, RoutedEventArgs a) { CmdRouter(Cmds.Home.ToString()); }
        private void Title_MouseDown(object s, MouseButtonEventArgs a) { CmdRouter(Cmds.Title.ToString()); }
        private void Title_MouseRightDown(object s, MouseButtonEventArgs a) { CmdRouter(Cmds.TitleStartShift.ToString()); }
        private void Title_MouseMove(object s, MouseEventArgs a) { CmdRouter(Cmds.TitleShift.ToString()); }
        private void Title_MouseRightButtonUp(object s, MouseEventArgs a) { CmdRouter(Cmds.TitleStopShift.ToString()); }     
        private void Mouse_LeftDown(object s, MouseButtonEventArgs a) { this.Activate(); }
        private void Key_Click(object s, KeyEventArgs a)
        {
            if (a.Key == Key.S && Keyboard.Modifiers == ModifierKeys.Control)
                CmdExecute(new Function(Cmds.Save.ToString(), Id, _user));
            else if (a.Key == Key.M && Keyboard.Modifiers == ModifierKeys.Control)
                CmdExecute(new Function(Cmds.Minimize.ToString(), Id, _user));
        }
        private void EventMessage(object s, EventArgs a)
        {
            var func = new Function(((MessageEventArgs)a).Message.Value.ToString(), Id, _user);
            if (!CheckAccess())
                Dispatcher.Invoke(() => CmdExecute(func));
            else
                CmdExecute(func);
        }
    }
}
